假如你是『无界』微前端框架的开发者 您所在的位置:网站首页 微前端 知乎 假如你是『无界』微前端框架的开发者

假如你是『无界』微前端框架的开发者

2024-01-26 20:07| 来源: 网络整理| 查看: 265

本文正在参加「金石计划」

传送门

之前写过一些微前端的文章,建议结合在一起阅读,本文将对比乾坤的实现方式进行讲解:

微前端技术选型

面试官:你了解微前端吗?

源码解析系列-qiankun源码详解

前言

在我们分析「无界」这个具体的微前端方案之前,我们先假设我们自己是框架设计者,思考一下一个微前端框架需要解决哪些问题;

给读者思考一分钟......

子应用如何加载,生命周期管理

js隔离措施

css隔离措施

通信机制

......

我们就以这些问题为切入点去看一看「无界」是怎么解决这些具体问题的。先回顾一下「无界」微前端怎么使用;

「无界」的使用方式

无界对于不同的前端框架都有对应的组件化封装。假如我们主项目是React技术栈,那么直接用WujieReact这个组件就可以了:

ReactDOM.render( , document.getElementById('root'), );

无界微前端没有什么改造成本,那么他是怎么做到的呢?接下来我们通过半源码、半手写代码的方式去分析「无界」这个微前端框架;

子应用如何加载,生命周期管理

子应用的加载与乾坤的方式相同,都通过一个importHTML函数进行加载;它的具体过程就是:

fetch我们传入的这个url,得到一个html字符串,然后通过正则表达式匹配到内部样式表、外部样式表、脚本;源码通过//gis匹配外部样式,通过/()[\s\S]*?/gi匹配脚本;通过/]*>[\s\S]*?/gi匹配内部样式;我们尝试一下自己写一个importHTML,解析一下我们当前这一篇文章:

const STYLE_REG = /(.*)/gi; const SCRIPT_REG = /(.*)/gi; const LINK_REG = //gi async function imoprtHTML() { let html = await fetch("https://juejin.cn/post/7209162467928096825"); html = await html.text(); const ans = html.replace(STYLE_REG, match=>{ // ... 很多逻辑 return match; } ).replace(SCRIPT_REG, match=>{ // ... 很多逻辑 return match; } ).replace(LINK_REG, match=>{ // ... 很多逻辑 debugger return match; }) }

第二步对于外部样式表、外部脚本我们也需要通过fetch获取到内容然后将代码存储起来

将合并的样式表添加到页面上截屏2023-03-20 17.24.52.png

执行js,这个详细过程我们后文分析截屏2023-03-20 17.29.48.png

子应用加载完毕

这个过程中涉及到哪些生命周期呢?

beforeLoad:子应用开始加载静态资源前触发,也就是importHTML之前触发 image.png beforeMount:子应用渲染前触发 (生命周期改造专用) image.png afterMount:子应用渲染后触发(生命周期改造专用) beforeUnmount:子应用卸载前触发(生命周期改造专用) afterUnmount:子应用卸载后触发(生命周期改造专用) activated:子应用进入后触发(保活模式专用) deactivated:子应用离开后触发(保活模式专用)

从上面我们就了解到了「无界」框架整体的一个加载流程,整体看起来它的生命周期比较多,比乾坤要多很多,但是比较好记,因为与Vue的生命周期在命名上有些类似;接下来我们具体分析一下js的隔离措施;

js沙箱

先回顾一下乾坤是怎么创建js沙箱的:乾坤设计了三种沙箱,在不支持Proxy的浏览器基于window进行diff,把修改了的变量存储到一个对象中,在子应用卸载时还原;支持Proxy的使用Proxy对window进行代理,然后存储修改了的变量;多个子应用共存的情况下会拷贝一份window对象;对拷贝的对象进行代理,子应用操作的也是这个拷贝的window对象;最后使用一个函数包裹子应用的js代码,绑定this和window,然后eval执行;这里使用到了eval执行代码,并且还使用到了with,这两个都是性能杀手,我们对比着看看「无界」怎么处理的?

「无界」是通过iframe进行环境隔离,也就是说我们的子应用上的全局对象其实是动态创建的一个iframe的全局对象,我们简单实现一下这个过程:

function iframePool() { const pool = []; return function() { const iframe = document.createElement("iframe"); pool.push(iframe); return { getIframe() { const ele = pool.pop(); return ele; }, recover(ele) { pool.push(ele); } } } } const container = iframePool()(); const iframe = container.getIframe(); document.body.appendChild(iframe); container.recover(iframe); window.a = 1234; const proxyWindow = iframe.contentWindow; const externalScripts = ` window.abc = 1; console.log(window.abc) ` function excute() { const doc = proxyWindow.document; const sc = doc.createElement("script"); sc.innerHTML = externalScripts; doc.body.appendChild(sc); console.log(proxyWindow.abc, "********") } excute();

简单解释一下这个过程:我们获取到了子应用代码之后将代码添加到iframe中,这样很简单地就实现了一个沙箱,然后子应用的全局环境绑定到iframe的window上就可以了,看起来很简单是不是?而且它也没有兼容性问题啊,但是iframe本身的一些问题可能会影响到主应用,这个需要我们具体去测评一下,iframe会阻塞页面的onload事件,通常用来加载广告这种无关紧要的内容,如果加载子应用会不会有性能问题,这个就需要不断的实践验证了

css隔离措施:在web-components容器下css可以自然隔离

通信机制

「无界」的通信机制是通过发布订阅模式实现的,就是创建一个全局的发布订阅者,然后挂载到了「无界」对象上:

image.png

这一块源码比较好实现,读者可以自行实现一个发布订阅模式

「无界」插件

「无界」中还使用了插件架构,主要分为html插件、css插件、js插件,html插件是在html解析完成之后执行,也就是上文提到的importHTML

image.png

cssLoader是在插入到DOM之前执行:

image.png

jsLoader插件在插入到iframe之前执行

image.png

当然还有一些其他的插件,无非都是在这些方法之前或者之后执行,这里列举一下:js-excludes、css-excludes、js-before-loaders、js-after-loaders、css-before-loaders、css-after-loaders

优雅降级

「无界」是存在优雅降级的,对于不支持webcomponents的浏览器,直接降级为iframe,虽然直接使用iframe体验上会差很多,但是目前已经有95%的浏览器已经支持webcomponents,所以影响的只是一些还在使用IE浏览器的用户,况且我们的主流框架也都抛弃了IE

image.png

后记

我们先站在高处思考了一下一个微前端框架应该解决什么问题;然后又从无界的使用、生命周期、js沙箱的创建、通信机制的实现、插件、优雅降级等几个方面详细地分析了一下「无界」内部的原理;「无界」和「乾坤」相比,它们是两种不同类型的微前端方案,一种是基于webcomponents,一种是基于路由,它们孰优孰劣需要结合项目自身的要求去分析;

从jQuery到三大框架经历了很长时间,那么一个完美的微前端方案需要等多长时间呢?



【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有